package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.model.SeckillGoods;
import com.atguigu.gmall.activity.model.UserRecode;
import com.atguigu.gmall.activity.service.SeckillBizService;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.rabbit.config.MqConst;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;

import static com.atguigu.gmall.common.constant.RedisConst.SECKILL_STOCK_PREFIX;


/**
 * @author: atguigu
 * @create: 2023-08-15 15:13
 */
@Slf4j
@Component
public class ActivityReceiver {

    @Autowired
    private SeckillGoodsService seckillGoodsService;

    @Autowired
    private SeckillBizService seckillBizService;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 监听每日凌晨发送秒杀预热消息，将当日参与秒杀，审核通过，秒杀数量>1放入缓存
     *
     * @param msg
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_TASK_1, durable = "true"),
            key = MqConst.ROUTING_TASK_1
    ))
    public void processSeckillToCache(String msg, Message message, Channel channel) {
        if (StringUtils.isNotBlank(msg)) {
            log.info("[秒杀服务]监听到秒杀商品预热消息，执行秒杀入库");
            String today = DateUtil.formatDate(new Date());
            //1.将当日参与秒杀，审核通过，秒杀数量大于1商品列表
            LambdaQueryWrapper<SeckillGoods> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SeckillGoods::getStatus, "1");
            queryWrapper.gt(SeckillGoods::getStockCount, 0);
            queryWrapper.apply("DATE_FORMAT(start_time, '%Y-%m-%d' ) = '" + today + "'");
            List<SeckillGoods> seckillGoodsList = seckillGoodsService.list(queryWrapper);

            //2.构建当日参与秒杀商品hash结构key,创建绑定hash操作对象
            String seckillKey = RedisConst.SECKILL_GOODS;
            BoundHashOperations<String, String, SeckillGoods> seckillHashOps = redisTemplate.boundHashOps(seckillKey);

            //3.遍历秒杀商品列表，将秒杀商品信息放入缓存Redis中hash中
            if (!CollectionUtils.isEmpty(seckillGoodsList)) {
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    String hashKey = seckillGoods.getSkuId().toString();
                    if (seckillHashOps.hasKey(hashKey)) {
                        //如果hash中包含该秒杀商品 结束本次循环；继续下一次循环
                        continue;
                    }
                    seckillHashOps.put(hashKey, seckillGoods);
                    //4.每遍历一次为当前秒杀商品设置库存
                    String stockKey = SECKILL_STOCK_PREFIX + hashKey;
                    //4.1 构建当前商品库存list的key 创建绑定List操作对象
                    BoundListOperations<String, String> stockListOps = redisTemplate.boundListOps(stockKey);
                    //4.2 循序库存数量，向库存list中添加元素
                    for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                        stockListOps.leftPush(hashKey);
                    }
                    //5.todo 广播所有秒杀节点，修改本地缓存中商品状态位
                    redisTemplate.convertAndSend("seckillpush", hashKey + ":1");
                }
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }


    /**
     * 监听秒杀队列，完成秒杀核心业务（处理秒杀）
     *
     * @param userRecode
     * @param channel
     * @param message
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER, durable = "true"),
            key = MqConst.ROUTING_SECKILL_USER
    ))
    public void processSeckillReq(UserRecode userRecode, Channel channel, Message message) {
        if (userRecode != null) {
            log.info("[秒杀服务]监听到秒杀请求：{}", userRecode);
            seckillBizService.processSeckillRequest(userRecode);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }


    /**
     * 监听秒杀商品扣减库存
     *
     * @param skuId
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_STOCK, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_SECKILL_STOCK, durable = "true"),
            key = MqConst.ROUTING_SECKILL_STOCK
    ))
    public void processDeductStock(Long skuId, Message message, Channel channel) {
        if (skuId != null) {
            log.info("[秒杀服务]监听秒杀扣减库存：{}", skuId);
            seckillBizService.processDeductStock(skuId);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }


    /**
     * 监听每天晚上发送秒杀情况缓存消息，将当日秒杀过程中产生缓存全部删除
     *
     * @param msg
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_TASK_18, durable = "true"),
            key = MqConst.ROUTING_TASK_18
    ))
    public void processSeckillClean(String msg, Message message, Channel channel) {
        if(StringUtils.isNotBlank(msg)){
            seckillBizService.processSeckillClean();
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }


}
